package cheme.Interpreter.AST.Arithmetic;

import cheme.Interpreter.AST.Interfaces.IAstBinaryOperation;
import cheme.Interpreter.AST.Interfaces.IAstExpression;
import cheme.Interpreter.AST.Interfaces.IAstType;
import cheme.Interpreter.AST.Interfaces.IObserver;
import cheme.Interpreter.AST.Interfaces.ISubject;
import cheme.Interpreter.AST.Interfaces.SubjectCore;

public abstract class AstBinaryOpAstNode implements IAstBinaryOperation
{
	IAstExpression lhs = null;
	IAstExpression rhs = null;
	
	private SubjectCore subjectCore = new SubjectCore(this);
	
	public AstBinaryOpAstNode()
	{
	}
	
	public AstBinaryOpAstNode(IAstExpression lhs, IAstExpression rhs)
	{
		super();
		this.lhs = lhs;
		this.rhs = rhs;
		
		if(null != lhs)
		{
			lhs.attach(this);
		}
		
		if(null != rhs)
		{
			rhs.attach(this);
		}
	}
	
	/**
	 * @return the lhs
	 */
	public IAstExpression getLhs()
	{
		return lhs;
	}

	/**
	 * @param lhs the lhs to set
	 */
	public void setLhs(IAstExpression lhs)
	{
		if(null != this.lhs)
		{
			this.lhs.detach(this);
		}
		
		if(null != this.lhs)
		{
			this.lhs.attach(this);
		}
		
		this.lhs = lhs;
	}

	/**
	 * @return the rhs
	 */
	public IAstExpression getRhs()
	{
		return rhs;
	}

	/**
	 * @param rhs the rhs to set
	 */
	public void setRhs(IAstExpression rhs)
	{
		if(null != this.rhs)
		{
			this.rhs.detach(this);
		}
		
		if(null != rhs)
		{
			rhs.attach(this);
		}
		
		this.rhs = rhs;
	}
	
	public ValueationState getValueationState()
	{
		if(null != getLhs() && null != getRhs())
		{
			if(ValueationState.Unknown == getLhs().getValueationState() ||
			   ValueationState.Unknown == getLhs().getValueationState())
			{
				return ValueationState.Unknown;
			}
			else if(ValueationState.Calculated == getLhs().getValueationState() ||
					ValueationState.Calculated == getLhs().getValueationState())
			{
				return ValueationState.Calculated;
			}
			else
			{
				return ValueationState.Evaluated;
			}
		}
		
		return ValueationState.Unknown;
	}

	public boolean isUnknown()
	{
		if(null != getLhs() && null != getRhs())
		{
			return getLhs().isUnknown() || getRhs().isUnknown();
		}
		
		/*
		 * Is it logical to have incomplete expressions (those with a null 
		 * operand) coinsidered as unknown values? Or should it be an error state?
		 * I am thinking the latter; 
		 */
		return false;
	}

	public void notify(ISubject s)
	{
		notifyObservers();
	}
	
	public void attach(IObserver o)
	{
		subjectCore.attach(o);
	}

	public void detach(IObserver o)
	{
		subjectCore.detach(o);
	}

	public void notifyObservers()
	{
		subjectCore.notifyObservers();
	}
	
	public IAstType getType()
	{
		assert null != getLhs();
		assert null != getRhs();
		
		IAstBinaryOperation type = null;
		
		try
		{
			if(null != getLhs() && null != getRhs())
			{
				IAstType lT = getLhs().getType();
				IAstType rT = getRhs().getType();
				if(null != lT && null != rT)
				{
					type = getClass().newInstance();
					type.setLhs(lT);
					type.setRhs(rT);
				}
				else if(null != lT)
				{
					return lT;
				}
				else if(null != rT)
				{
					return rT;
				}
			}
		}
		catch(Exception e)
		{
		}
		
		return type;
	}
}
